Skip to content

Extract shared export engine; wire CLI to summary cache and drop mypy…#112

Merged
wpak-ai merged 6 commits into
masterfrom
refactor/export-engine-consolidation
Jun 23, 2026
Merged

Extract shared export engine; wire CLI to summary cache and drop mypy…#112
wpak-ai merged 6 commits into
masterfrom
refactor/export-engine-consolidation

Conversation

@clean6378-max-it

@clean6378-max-it clean6378-max-it commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Closes #106

Summary

Finishes the export-script consolidation started in #42 / PR #61 by extracting shared orchestration into services/export_engine.py, reducing scripts/export.py to a thin CLI wrapper, and routing the CLI through the same cached workspace-resolution path as the web layer (summary cache + mtime fingerprinting). Removes the scripts.export mypy strict override.

Continues cppa-cursor-browser #42. Companion PR (Monday #4): os.environ workspace-path bypass fix — land after this so explicit-override plumbing slots into the shared engine.

Changes

  • New services/export_engine.py — shared orchestration:
    • prepare_workspace_orchestration() — workspace scan, mtime fingerprint, resolve_workspace_context_cached
    • load_global_db_export_data() — global DB maps, composer rows, invalid-workspace aliases
    • collect_export_entries() — IDE + CLI conversation collection, exclusion filtering, markdown generation
  • scripts/export.py — reduced to arg parsing, manifest/state I/O, zip/file output; delegates orchestration to collect_export_entries()
  • services/workspace_listing.py — uses prepare_workspace_orchestration() instead of duplicating fingerprint + context setup
  • pyproject.toml — removed [[tool.mypy.overrides]] ignore_errors = true for scripts.export

Out of scope

Acceptance criteria

  • scripts/export.py calls shared export orchestration instead of reimplementing workspace scanning, DB reading, and composer processing
  • Summary cache and mtime fingerprinting available to CLI path
  • scripts.export mypy override removed
  • scripts/export.py passes mypy --strict
  • Existing CLI export tests pass
  • No behavioral change to CLI export output
  • PR approved by at least 1 reviewer

Test plan

  • mypy --strict scripts/export.py services/export_engine.py services/workspace_listing.py
  • pytest tests/test_cli_export_e2e.py tests/test_export_exclusion_filtering.py tests/test_cli_args.py tests/test_api_export.py -q
  • pytest tests/test_api_workspaces.py tests/test_workspace_listing_*.py -q
  • python scripts/export.py --help

References

Summary by CodeRabbit

  • Refactor
    • Centralized chat export orchestration for both IDE/global composer and CLI sessions into a shared export engine.
    • Updated the export API to delegate export generation to the engine, handle since=last cutoff via export state, and build zip contents from returned entries.
    • Streamlined the export script and workspace listing to reuse shared orchestration, caching, and consistent markdown output.
  • Bug Fixes
    • Improved resilience by skipping corrupted/non-conforming composer data.
    • Added workspace path override precedence to prevent incorrect workspace resolution.
  • Chores
    • Re-enabled mypy type-checking for the export script.
  • Tests
    • Expanded coverage for API body validation, export engine behavior, and export-state cutoff logic.

@clean6378-max-it clean6378-max-it self-assigned this Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A new services/export_engine.py module is added to orchestrate both IDE (composer) and CLI chat exports through a shared service layer with typed data models and workspace fingerprinting for caching. scripts/export.py is refactored from ~540 lines to a thin typed CLI wrapper that parses arguments, resolves workspace paths, and delegates to collect_export_entries. services/workspace_listing.py and api/export_api.py are updated to use the same shared orchestration. utils/workspace_path.py gains an explicit override parameter. The mypy ignore_errors override for scripts.export in pyproject.toml is removed to enable strict type checking on the CLI script.

Changes

Export Orchestration Consolidation and Refactoring

Layer / File(s) Summary
Export models and types
models/export.py, models/__init__.py
Adds CollectedExportEntry TypedDict with fields for export identifiers, paths, content, metadata timestamps, title, and workspace; updates models/__init__.py to import and export CollectedExportEntry as part of the public API.
Export engine module setup and utilities
services/export_engine.py
Establishes the new shared export orchestration module with SinceMode type alias and read_last_export_ms helper to compute the "since last export" cutoff timestamp from state dict or JSON file.
Workspace orchestration preparation and caching
services/export_engine.py
Defines WorkspaceOrchestration and GlobalDbExportData frozen dataclasses; implements build_workspace_display_maps and prepare_workspace_orchestration to fingerprint workspace storage, resolve cached WorkspaceContext, and enable downstream filtering and caching.
IDE global-DB loading for export data enrichment
services/export_engine.py
Implements load_global_db_export_data to conditionally open the Cursor global storage DB, enrich workspace context, load project layouts, bubble/code-diff maps, and composer rows, infer invalid workspace alias mappings, and return None when the global DB is unavailable.
IDE composer export entry collection
services/export_engine.py
Implements _collect_ide_export_entries to iterate composer rows with best-effort JSON parsing, compute updated timestamps with lastUpdatedAt fallback to createdAt, filter by since and last_export_ms, resolve workspace/project assignment with invalid-alias inference, build searchable text from bubble/model/config/diff data, apply exclusion rules, render markdown, and emit deterministic CollectedExportEntry records.
CLI session export entry collection
services/export_engine.py
Implements _collect_cli_export_entries to enumerate CLI projects/sessions, compute per-session timestamps as max(createdAt, db mtime), filter by since, traverse session DB blobs into bubbles with per-session error handling, derive titles from metadata or first user text, build searchable text, apply exclusion rules, render markdown, and emit deterministic CollectedExportEntry records.
Top-level export orchestration
services/export_engine.py
Implements collect_export_entries orchestrator that prepares workspace orchestration once, conditionally includes IDE exports only when global DB data loads successfully, conditionally includes CLI exports, and returns the combined list of CollectedExportEntry.
scripts/export.py refactored as typed CLI wrapper
scripts/export.py
Adds ExportCliOptions TypedDict for parsed CLI options; updates load_manifest_entries, write_manifest_entries, and parse_args with explicit type signatures; restructures parse_args with detailed argument definitions preserving flag semantics; rewrites main() to resolve workspace_path via --base-dir override, compute last_export via read_last_export_ms, call collect_export_entries, write zip or markdown files, and update per-run and global manifests with conditional ISO timestamp serialization.
api/export_api.py refactored to delegate to shared engine
api/export_api.py
Updates imports to remove in-file DB/text dependencies and add export-engine helpers; refactors export_chats endpoint to validate JSON body type, normalize since, resolve workspace path, verify global DB file exists, delegate to collect_export_entries with exclusion rules and computed last_export_ms, build zip using entry["rel_path"] and entry["content"], and return 404 when no conversations exported.
services/workspace_listing.py updated to use shared orchestration
services/workspace_listing.py
Replaces direct fingerprinting and context-resolution imports with WorkspaceOrchestration and prepare_workspace_orchestration; refactors list_workspace_projects to build shared orchestration up front using orch.fingerprint for cache lookups; updates _build_workspace_projects_uncached to accept orch parameter and derive ctx and workspace_entries from it.
utils/workspace_path.py adds explicit override parameter
utils/workspace_path.py
Extends resolve_workspace_path to accept optional kw-only override parameter; returns early when override is provided (with tilde expansion), otherwise snapshots module-level override under lock; preserves WORKSPACE_PATH environment-variable handling and fallback behavior.
Testing infrastructure and mypy configuration
pyproject.toml, services/export_engine.py, tests/test_export_engine.py, tests/test_api_export.py, tests/test_workspace_path_thread_safety.py
Removes [[tool.mypy.overrides]] block for scripts.export to enable strict type checking; adds comprehensive unit tests covering read_last_export_ms behavior, collect_export_entries nocache environment variable handling, corrupt composer row skipping, IDE export timestamp fallback, workspace display name fallback, and exclusion rules filtering; updates test_api_export.py patch target and adds non-dict JSON body validation test; adds regression test verifying explicit workspace path override takes precedence over module-level override.

Sequence Diagram(s)

sequenceDiagram
  participant User as CLI/API Caller
  participant Script as scripts/export.py or api/export_api.py
  participant Engine as collect_export_entries
  participant IDEExp as _collect_ide_export_entries
  participant CLIExp as _collect_cli_export_entries
  participant Filter as Exclusion Filter

  User->>Script: request export (since, include_composer, include_cli)
  Script->>Script: resolve workspace_path, load exclusion_rules, compute last_export_ms
  Script->>Engine: collect_export_entries(workspace_path, rules, since, last_export_ms)
  Engine->>Engine: prepare_workspace_orchestration(fingerprint for caching)
  alt include_composer=True
    Engine->>IDEExp: load_global_db_export_data(orch)
    IDEExp-->>Engine: GlobalDbExportData with composer rows
    Engine->>IDEExp: _collect_ide_export_entries(orch, global_db_data, since, last_export_ms)
    IDEExp->>IDEExp: iterate rows → parse JSON → filter by since
    IDEExp->>Filter: build bubble/model/config/diff text + apply rules
    Filter-->>Engine: CollectedExportEntry[]
  end
  alt include_cli=True
    Engine->>CLIExp: _collect_cli_export_entries(orch, since, last_export_ms)
    CLIExp->>CLIExp: enumerate projects/sessions → traverse blobs → build text
    CLIExp->>Filter: apply exclusion rules
    Filter-->>Engine: CollectedExportEntry[]
  end
  Engine-->>Script: combined list[CollectedExportEntry]
  Script->>Script: write zip or markdown + update manifests
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • cppalliance/cppa-cursor-browser#61: Prior refactor that delegated workspace scanning and markdown generation to shared service helpers; this PR continues by extracting the remaining orchestration loop into services/export_engine.py.
  • cppalliance/cppa-cursor-browser#8: Added CLI agent session chat reader/exporter plumbing that the refactored services/export_engine.collect_export_entries now orchestrates for both IDE and CLI paths.
  • cppalliance/cppa-cursor-browser#30: Earlier changes to scripts/export.py manifest.jsonl handling; this PR refactors the same manifest I/O with updated field types and conditional updated_at timestamp serialization.

Suggested reviewers

  • wpak-ai

Poem

🐇 Hoppity-hop, the export paths align!
One shared engine, both CLI and web combine.
The script shrunk down to a wrapper so neat,
Mypy says strict—now the logic is sweet!
Orchestration shared, no duplication in sight! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: extracting a shared export engine, consolidating orchestration, and removing the mypy strict-mode exemption for scripts/export.
Linked Issues check ✅ Passed The PR successfully addresses all acceptance criteria from issue #106: shared export-orchestration function created and used by both paths, mypy override removed, scripts/export passes strict checking, and existing tests continue to pass.
Out of Scope Changes check ✅ Passed All changes directly support the core objective of consolidating export orchestration and removing the mypy exemption; no unrelated changes detected outside issue #106 scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/export-engine-consolidation

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
services/export_engine.py (1)

91-92: 📐 Maintainability & Code Quality | 🔵 Trivial

Justify broad exception handlers with targeted comments.

The three except Exception handlers at lines 91, 340, and 378 are defensible as intentional robustness boundaries. Add # noqa: BLE001 comments with brief justifications:

  • Line 91: Best-effort fallback serialization (json.dumpsstr()).
  • Line 340: Defensive enumeration of CLI chats; any error is logged and enumeration is skipped.
  • Line 378: Defensive iteration of CLI sessions; any error is logged and session is skipped.

Each already logs the exception with exc_info=True for observability, making them suitable for broad catches in boundary functions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/export_engine.py` around lines 91 - 92, Add `# noqa: BLE001`
comments to justify the three broad `except Exception` handlers in the
export_engine.py file. For the handler around line 91 that provides best-effort
fallback serialization using str() when json.dumps fails, add a comment
explaining this is an intentional robustness boundary for serialization. For the
handler around line 340 that defensively enumerates CLI chats with logging, add
a comment explaining any error is logged and enumeration is skipped. For the
handler around line 378 that defensively iterates over CLI sessions with
logging, add a comment explaining any error is logged and the session is
skipped. Each comment should be brief and positioned on the same line or
immediately before the except Exception clause to acknowledge that these broad
catches are intentional and already provide observability through exc_info=True
logging.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/export.py`:
- Around line 177-181: The code at line 180 assumes the `lastExportTime` value
is always a string and calls `.replace()` directly on it, but it could be a
numeric timestamp or other format in a valid JSON state file, causing a crash.
Replace the manual datetime parsing and string manipulation (the `.replace()`
call and `datetime.fromisoformat()` logic) with the existing `to_epoch_ms()`
helper function, which safely handles various timestamp formats and variants.
Pass the raw `ts` value directly to `to_epoch_ms()` instead of trying to parse
it as a string first.

In `@services/export_engine.py`:
- Around line 217-227: The current code only catches JSON parsing errors but
doesn't validate the structure of successfully parsed data. After the try-except
block for json.loads in the composerData processing, add type validation to
ensure that cd is actually a dictionary before calling cd.get, and validate that
headers extracted from cd.get("fullConversationHeadersOnly") is a list of valid
dictionaries before accessing h.get on individual items. If cd is not a dict, or
if headers contains non-dict items, log as corrupt and continue to the next row,
matching the defensive pattern used elsewhere. Apply the same validation pattern
to the similar code section also mentioned around lines 272-274.
- Around line 405-413: The tool_call_texts list comprehension is extracting
tc.get("input") and tc.get("summary") values which can be non-string objects,
causing the subsequent "\n\n".join() call to fail. Normalize both the input and
summary fields by wrapping them with json_dump_safe before they are added to the
tool_call_texts list, ensuring all values are properly serialized to strings
before the join operation.

In `@services/workspace_listing.py`:
- Around line 93-104: The prepare_workspace_orchestration call on line 93 is
being passed the raw nocache parameter, but it should be passed the effective
no-cache flag that accounts for both the request parameter and the
CURSOR_CHAT_BROWSER_NOCACHE environment variable. Calculate the effective
no-cache flag by calling nocache_enabled(request_nocache=nocache) before
invoking prepare_workspace_orchestration, and then pass this effective flag
value to prepare_workspace_orchestration instead of the raw nocache parameter.
This ensures the orchestration respects the same no-cache logic that is applied
to the cache operations later in the function.

---

Nitpick comments:
In `@services/export_engine.py`:
- Around line 91-92: Add `# noqa: BLE001` comments to justify the three broad
`except Exception` handlers in the export_engine.py file. For the handler around
line 91 that provides best-effort fallback serialization using str() when
json.dumps fails, add a comment explaining this is an intentional robustness
boundary for serialization. For the handler around line 340 that defensively
enumerates CLI chats with logging, add a comment explaining any error is logged
and enumeration is skipped. For the handler around line 378 that defensively
iterates over CLI sessions with logging, add a comment explaining any error is
logged and the session is skipped. Each comment should be brief and positioned
on the same line or immediately before the except Exception clause to
acknowledge that these broad catches are intentional and already provide
observability through exc_info=True logging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b6a37b1-08cf-4298-bc5c-080a012d1e3d

📥 Commits

Reviewing files that changed from the base of the PR and between ee6a6e7 and 2b76dd5.

📒 Files selected for processing (4)
  • pyproject.toml
  • scripts/export.py
  • services/export_engine.py
  • services/workspace_listing.py
💤 Files with no reviewable changes (1)
  • pyproject.toml

Comment thread scripts/export.py Outdated
Comment thread services/export_engine.py
Comment thread services/export_engine.py
Comment thread services/workspace_listing.py Outdated
Use to_epoch_ms for lastExportTime parsing, validate composerData shape,
serialize CLI tool-call fields safely, and pass effective nocache flag
through workspace listing orchestration.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/export_api.py`:
- Around line 97-99: The code assumes `body` is a dictionary when accessing
`body.get("since")`, but if a client sends a JSON array or string instead of a
JSON object, this will raise an AttributeError and return a 500 error. Before
attempting to read `body.get("since")`, add a validation check to ensure `body`
is actually a dictionary. If it is not a dictionary, return an appropriate 400
Bad Request error response instead of attempting to process it further. This
validation should occur before the line where the `since` variable is assigned.

In `@tests/test_export_engine.py`:
- Around line 33-37: Replace all hardcoded /tmp directory paths like "/tmp/ws"
and "/tmp/out" with dynamically generated temporary directory paths using the
tempfile module. Import tempfile at the top of the test file, then use
tempfile.TemporaryDirectory() or tempfile.gettempdir() to generate safe
temporary paths for the workspace_path and out_dir parameters that are passed to
the function being tested. This applies to all occurrences mentioned in the test
file including lines 33-37, 53, and 83-87 where these hardcoded paths appear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a032144-b2ea-4385-82c2-f3cbb10c734d

📥 Commits

Reviewing files that changed from the base of the PR and between d75620d and 45c36ac.

📒 Files selected for processing (7)
  • api/export_api.py
  • scripts/export.py
  • services/export_engine.py
  • tests/test_api_export.py
  • tests/test_export_engine.py
  • tests/test_workspace_path_thread_safety.py
  • utils/workspace_path.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/export_engine.py
  • scripts/export.py

Comment thread api/export_api.py
Comment thread tests/test_export_engine.py Outdated
Comment thread scripts/export.py
Comment thread services/export_engine.py Outdated
Comment thread services/export_engine.py Outdated
Comment thread services/export_engine.py Outdated
Comment thread tests/test_export_engine.py
Comment thread api/export_api.py Outdated
@timon0305 timon0305 mentioned this pull request Jun 23, 2026
10 tasks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
services/export_engine.py (1)

256-260: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep IDE timestamp semantics aligned with the no-behavior-change contract.

Line 259 explicitly says this fallback changes timestamps, filenames, and --since last, which conflicts with the PR objective to preserve CLI export output. Either restore the legacy lastUpdatedAt-only behavior here or update the acceptance criteria/release notes to call out the intentional output change.

Proposed fix to preserve legacy timestamp behavior
-        updated_at = to_epoch_ms(cd.get("lastUpdatedAt")) or to_epoch_ms(
-            cd.get("createdAt"),
-        )
-        # Intentional behavior change vs legacy CLI: fall back to createdAt when
-        # lastUpdatedAt is absent (affects timestamps, filenames, and --since last).
+        updated_at = to_epoch_ms(cd.get("lastUpdatedAt"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/export_engine.py` around lines 256 - 260, The updated_at assignment
includes a fallback from lastUpdatedAt to createdAt which contradicts the stated
no-behavior-change objective of the PR. To preserve legacy CLI behavior, remove
the fallback logic in the updated_at assignment so it uses only lastUpdatedAt
without falling back to createdAt. This will ensure that timestamps, filenames,
and --since last operations maintain the same behavior as the original CLI
implementation.
🧹 Nitpick comments (1)
scripts/export.py (1)

181-183: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Create state_dir before all state-directory writes.

global_manifest_path is written before the explicit os.makedirs(state_dir, exist_ok=True). Move that creation immediately after Line 181 so fresh --no-zip runs do not depend on write_manifest_entries() creating parents.

♻️ Proposed fix
     state_dir = get_global_state_dir()
+    os.makedirs(state_dir, exist_ok=True)
     state_path = os.path.join(state_dir, "export_state.json")
     last_export = read_last_export_ms(since, state_path=state_path)
@@
-    os.makedirs(state_dir, exist_ok=True)
     with open(os.path.join(state_dir, "export_state.json"), "w", encoding="utf-8") as f:
         json.dump(state, f, indent=2)

Also applies to: 233-248, 256-256

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/export.py` around lines 181 - 183, The state directory is not created
before files are written to it. Move the os.makedirs(state_dir, exist_ok=True)
call to immediately after the state_dir assignment from get_global_state_dir()
at line 181, before any writes to state_dir such as global_manifest_path. Apply
this same fix to all other locations where state_dir writes occur (around lines
233-248 and 256) to ensure the directory exists before any file operations
attempt to use it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@services/export_engine.py`:
- Around line 256-260: The updated_at assignment includes a fallback from
lastUpdatedAt to createdAt which contradicts the stated no-behavior-change
objective of the PR. To preserve legacy CLI behavior, remove the fallback logic
in the updated_at assignment so it uses only lastUpdatedAt without falling back
to createdAt. This will ensure that timestamps, filenames, and --since last
operations maintain the same behavior as the original CLI implementation.

---

Nitpick comments:
In `@scripts/export.py`:
- Around line 181-183: The state directory is not created before files are
written to it. Move the os.makedirs(state_dir, exist_ok=True) call to
immediately after the state_dir assignment from get_global_state_dir() at line
181, before any writes to state_dir such as global_manifest_path. Apply this
same fix to all other locations where state_dir writes occur (around lines
233-248 and 256) to ensure the directory exists before any file operations
attempt to use it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e74ccea-2cd0-4707-b79c-556057db1761

📥 Commits

Reviewing files that changed from the base of the PR and between d3b101f and 38e548d.

📒 Files selected for processing (6)
  • api/export_api.py
  • models/__init__.py
  • models/export.py
  • scripts/export.py
  • services/export_engine.py
  • tests/test_export_engine.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/export_api.py

@clean6378-max-it

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit, @timon0305 's comments are addressed now?

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="cppalliance/cppa-cursor-browser"
PR=112

echo "=== PR Review Comments by timon0305 ==="
gh api "repos/$REPO/pulls/$PR/comments" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    path: .path,
    line: (.line // .original_line),
    body,
    created_at,
    updated_at,
    url: .html_url
  }'

echo ""
echo "=== Issue Comments by timon0305 ==="
gh api "repos/$REPO/issues/$PR/comments" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    body,
    created_at,
    updated_at,
    url: .html_url
  }'

echo ""
echo "=== PR Reviews by timon0305 ==="
gh api "repos/$REPO/pulls/$PR/reviews" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    state,
    body,
    submitted_at,
    url: .html_url
  }'

Length of output: 3768


🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="cppalliance/cppa-cursor-browser"
PR=112

echo "=== All PR Review Comments (with resolution status) ==="
gh api "repos/$REPO/pulls/$PR/comments" \
  --paginate \
  --jq '.[] | {
    id,
    in_reply_to_id,
    user: .user.login,
    path: .path,
    line: (.line // .original_line),
    body: (.body | .[0:200]),
    created_at,
    url: .html_url
  }'

echo ""
echo "=== Resolved review threads via GraphQL ==="
gh api graphql -F owner="cppalliance" -F name="cppa-cursor-browser" -F pr=112 -f query='
query($owner: String!, $name: String!, $pr: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $pr) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          path
          line
          comments(first: 3) {
            nodes {
              author { login }
              body
              createdAt
            }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | {
  isResolved,
  path: .path,
  line: .line,
  first_comment_author: .comments.nodes[0].author.login,
  first_comment_body: (.comments.nodes[0].body | .[0:150]),
  reply_count: (.comments.nodes | length)
}'

Length of output: 10575



@clean6378-max-it Here's a breakdown of @timon0305's 6 comments and their current status. None of the threads are marked as resolved yet, but based on the commit and AI diff summaries:

# File / Location Comment Status
1 scripts/export.py line 179 What's still left for the companion PR? Answered — your reply explains PR #114 covers the remainder. No code change needed.
2 services/export_engine.py line 52 CollectedExportEntry is a duplicate of models.ExportEntry — import from models instead Appears addressedCollectedExportEntry is now in models/export.py and re-exported from models/__init__.py.
3 services/export_engine.py line 239 updatedAt → createdAt fallback is a silent behavior change — add a test + fix "no behavioral change" wording ⚠️ Partially addressed — the test was added, but the PR description still says "no behavioral change to export output," which is now inaccurate. The description wording should be updated.
4 services/export_engine.py line 328 rel_path rebuilds the same path as out_path; use os.path.relpath(out_path, out_dir) instead Status unclear — the AI summaries don't mention this nit being fixed. Worth double-checking.
5 tests/test_export_engine.py line 114 Needs more tests: updatedAt→createdAt fallback, display-name fallback, exclusion filtering Appears addressed — all three tests were added per the AI summary.
6 api/export_api.py line 70 _read_last_export_ms duplicated in scripts/export.py — make it a shared helper Appears addressedread_last_export_ms is now in services/export_engine.py and used in both paths.

Remaining actions before @timon0305 can re-review:

  • Verify comment 4 (rel_path nit) is fixed or leave a reply if intentionally skipped.
  • Update the PR description to remove the "no behavioral change" claim (or clarify it's behavioral only for entries that previously had no lastUpdatedAt).
  • Resolve threads that are code-fixed (2, 5, 6) so @timon0305 can focus on the open items.

@clean6378-max-it

Copy link
Copy Markdown
Collaborator Author

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain
🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="cppalliance/cppa-cursor-browser"
PR=112

echo "=== PR Review Comments by timon0305 ==="
gh api "repos/$REPO/pulls/$PR/comments" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    path: .path,
    line: (.line // .original_line),
    body,
    created_at,
    updated_at,
    url: .html_url
  }'

echo ""
echo "=== Issue Comments by timon0305 ==="
gh api "repos/$REPO/issues/$PR/comments" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    body,
    created_at,
    updated_at,
    url: .html_url
  }'

echo ""
echo "=== PR Reviews by timon0305 ==="
gh api "repos/$REPO/pulls/$PR/reviews" \
  --paginate \
  --jq '.[] | select(.user.login == "timon0305") | {
    id,
    state,
    body,
    submitted_at,
    url: .html_url
  }'

Length of output: 3768

🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="cppalliance/cppa-cursor-browser"
PR=112

echo "=== All PR Review Comments (with resolution status) ==="
gh api "repos/$REPO/pulls/$PR/comments" \
  --paginate \
  --jq '.[] | {
    id,
    in_reply_to_id,
    user: .user.login,
    path: .path,
    line: (.line // .original_line),
    body: (.body | .[0:200]),
    created_at,
    url: .html_url
  }'

echo ""
echo "=== Resolved review threads via GraphQL ==="
gh api graphql -F owner="cppalliance" -F name="cppa-cursor-browser" -F pr=112 -f query='
query($owner: String!, $name: String!, $pr: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $pr) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          path
          line
          comments(first: 3) {
            nodes {
              author { login }
              body
              createdAt
            }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | {
  isResolved,
  path: .path,
  line: .line,
  first_comment_author: .comments.nodes[0].author.login,
  first_comment_body: (.comments.nodes[0].body | .[0:150]),
  reply_count: (.comments.nodes | length)
}'

Length of output: 10575

@clean6378-max-it Here's a breakdown of @timon0305's 6 comments and their current status. None of the threads are marked as resolved yet, but based on the commit and AI diff summaries:

File / Location Comment Status

1 scripts/export.py line 179 What's still left for the companion PR? ✅ Answered — your reply explains PR #114 covers the remainder. No code change needed.
2 services/export_engine.py line 52 CollectedExportEntry is a duplicate of models.ExportEntry — import from models instead ✅ Appears addressedCollectedExportEntry is now in models/export.py and re-exported from models/__init__.py.
3 services/export_engine.py line 239 updatedAt → createdAt fallback is a silent behavior change — add a test + fix "no behavioral change" wording ⚠️ Partially addressed — the test was added, but the PR description still says "no behavioral change to export output," which is now inaccurate. The description wording should be updated.
4 services/export_engine.py line 328 rel_path rebuilds the same path as out_path; use os.path.relpath(out_path, out_dir) instead ❓ Status unclear — the AI summaries don't mention this nit being fixed. Worth double-checking.
5 tests/test_export_engine.py line 114 Needs more tests: updatedAt→createdAt fallback, display-name fallback, exclusion filtering ✅ Appears addressed — all three tests were added per the AI summary.
6 api/export_api.py line 70 _read_last_export_ms duplicated in scripts/export.py — make it a shared helper ✅ Appears addressedread_last_export_ms is now in services/export_engine.py and used in both paths.
Remaining actions before @timon0305 can re-review:

  • Verify comment 4 (rel_path nit) is fixed or leave a reply if intentionally skipped.
  • Update the PR description to remove the "no behavioral change" claim (or clarify it's behavioral only for entries that previously had no lastUpdatedAt).
  • Resolve threads that are code-fixed (2, 5, 6) so @timon0305 can focus on the open items.

rel_path nit (line ~328) — already fixed
Current code uses os.path.relpath in both IDE and CLI paths:

No further action needed. The “status unclear” note is wrong — this was fixed in the earlier #112 review round.

createdAt fallback — resolved the other way
After removing the fallback, “no behavioral change” is correct.

@timon0305 timon0305 requested a review from wpak-ai June 23, 2026 18:39
@wpak-ai wpak-ai merged commit e2660d4 into master Jun 23, 2026
16 checks passed
@wpak-ai wpak-ai deleted the refactor/export-engine-consolidation branch June 23, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cppa-cursor-browser: Export script — consolidate orchestration + wire summary cache for CLI

3 participants